package com.hz.cheetah.server;


import com.hz.cheetah.common.bean.RpcRequest;
import com.hz.cheetah.common.bean.RpcResponse;
import com.hz.cheetah.common.codec.RpcDecoder;
import com.hz.cheetah.common.codec.RpcEncoder;
import com.hz.cheetah.common.net.NetUtil;
import com.hz.cheetah.common.sys.JvmRuntime;
import com.hz.cheetah.common.util.StringUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.hz.cheetah.hub.ServiceRegistry;

import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

/**
 * RPC 服务器（用于发布 RPC 服务）
 *TODO 1:未完成自动判断ip 自动生成端口，2:可配置的boss线程数，可配置的工作线程数量，3 日志需要异步存储
 * @author yangchuan
 * @since 1.0.0
 */
public class CheetahServer implements ApplicationContextAware, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(CheetahServer.class);

    private String serviceAddress;

    private ServiceRegistry serviceRegistry;

    private int nioWorkerNum =  JvmRuntime.getRuntime().getCPUsNumber() * 2;
    private int acceptProcessNum = 1;
    /**
     * 存放 服务名 与 服务对象 之间的映射关系
     */
    private Map<String, Object> handlerMap = new HashMap<>();

    public CheetahServer(){

    }

    public void setServiceAddress(String serviceAddress) {
        this.serviceAddress = serviceAddress;
    }

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    public CheetahServer(String serviceAddress) {
        this.serviceAddress = serviceAddress;
    }

    public CheetahServer(String serviceAddress, ServiceRegistry serviceRegistry) {

        this.serviceAddress = serviceAddress;
        this.serviceRegistry = serviceRegistry;
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        // 扫描带有 RpcService 注解的类并初始化 handlerMap 对象
        Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(CheetahService.class);
        if (MapUtils.isNotEmpty(serviceBeanMap)) {
            for (Object serviceBean : serviceBeanMap.values()) {
                CheetahService rpcService = serviceBean.getClass().getAnnotation(CheetahService.class);
                String serviceName = rpcService.value().getName();
                String serviceVersion = rpcService.version();
                if (StringUtil.isNotEmpty(serviceVersion)) {
                    serviceName += "-" + serviceVersion;
                }
                handlerMap.put(serviceName, serviceBean);
            }
        }
    }

    @Override
    public void afterPropertiesSet() {
        final EventLoopGroup bossGroup = new NioEventLoopGroup(acceptProcessNum);
        final EventLoopGroup workerGroup = new NioEventLoopGroup(nioWorkerNum);
        MessageQueueHandler handler = new MessageQueueHandler(0,300*1000);

        new Thread(() -> {
                try {
                    // 创建并初始化 Netty 服务端 Bootstrap 对象
                    ServerBootstrap bootstrap = new ServerBootstrap();
                    bootstrap.group(bossGroup, workerGroup);
                    bootstrap.channel(NioServerSocketChannel.class);
                    bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new RpcDecoder(RpcRequest.class)); // 解码 RPC 请求
                            pipeline.addLast(new RpcEncoder(RpcResponse.class)); // 编码 RPC 响应
                            pipeline.addLast(new CheetahServerHandler(handlerMap, handler)); // 处理 RPC 请求
                        }
                    });
                    bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
                    bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
                    // 获取 RPC 服务器的 IP 地址与端口号
                    String[] addressArray = StringUtil.split(serviceAddress, ":");
                    String ip = addressArray[0];
                    int port = Integer.parseInt(addressArray[1]);
                    if ("0.0.0.0".equals(ip)) {
                        ip = NetUtil.getLocalIP4Addr();
                    }
                    // 启动 RPC 服务器
                    ChannelFuture future = bootstrap.bind(ip, port).sync();
                    // 注册 RPC 服务地址
                    if (serviceRegistry != null) {
                        for (String interfaceName : handlerMap.keySet()) {
                            serviceRegistry.register(interfaceName, ip + ":" + port);
                            LOGGER.debug("register service: {} => {}", interfaceName, ip + ":" + port);
                        }
                    }
                    LOGGER.debug("server started on port {}", port);
                    // 关闭 RPC 服务器
                    future.channel().closeFuture().sync();
                }catch (Exception e){
                    throw new RuntimeException(e);
                } finally {
                    workerGroup.shutdownGracefully();
                    bossGroup.shutdownGracefully();
                }
            }
        ).start();
    }
}
